////
//  LBTexture.mm
//  OpenGLGame
//
//  Created by Nguyen Cong Huy on 11/12/12.
//  Copyright (c) 2012 UIT. All rights reserved.
//

#include <iostream>
#include "LBTexture.h"
#include "LBGraphics.h"
#include "Matrix.h"
#include "LBUtils.h"

LBTexture::LBTexture(){
    
}
LBTexture::LBTexture(int width, int height){
    this->width = width;
    this->height = height;
    
    dWidth = width;
    dHeight = height;
    
    mWorld = new Matrix();
    
    glGenTextures(1, &texId);
    glBindTexture(GL_TEXTURE_2D, texId);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    
    resetSourceRect();
    
    LBGraphics* g = LBGraphics::getInstance();
    calculateVertices();
    
    g->useDefaultProgram();
    setLocationAndScale(0, 0, 1, 1);
    
    textureActive = GL_TEXTURE0;
    isUseDefaultUniform = true;
    byteInAPixel = 4;
    angle = 0;
    xScale = yScale = 1.0f;
    isDeleteTexIdWhenDeleteObj = true;
    
}

LBTexture::LBTexture(const unsigned char* imageData, int width, int height){
    this->width = width;
    this->height = height;
    
    dWidth = width;
    dHeight = height;
    
    mWorld = new Matrix();
    
    glGenTextures(1, &texId);
    glBindTexture(GL_TEXTURE_2D, texId);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    
    resetSourceRect();
    
    LBGraphics* g = LBGraphics::getInstance();
    calculateVertices();
    
    g->useDefaultProgram();
    setLocationAndScale(0, 0, 1, 1);
    
    positionAttribute = g->getPositionAttribute();
    texCoorAttribute = g->getTexCoorAttribute();
    textureUniform = g->getTextureUniform();
    
    textureActive = GL_TEXTURE0;
    isUseDefaultUniform = true;
    byteInAPixel = 4;
    angle = 0;
    xScale = yScale = 1.0f;
    isDeleteTexIdWhenDeleteObj = true;
}

LBTexture::LBTexture(const unsigned char* imageData, int width, int height, int bytePerPixel){
    this->width = width;
    this->height = height;
    
    int colorType = GL_RGBA;
    if(bytePerPixel == 4)
        colorType = GL_RGBA;
    else colorType = GL_RGB;
    
    mWorld = new Matrix();
    
    sX = dX = sY = dY = 0;
    sWidth = dWidth = width;
    sHeight = dHeight = height;
    
    dWidth = width;
    dHeight = height;
    
    glGenTextures(1, &texId);
    glBindTexture(GL_TEXTURE_2D, texId);
    glTexImage2D(GL_TEXTURE_2D, 0, colorType, width, height, 0, colorType, GL_UNSIGNED_BYTE, imageData);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    
    imageVertices[0] = 0.0f;
    imageVertices[1] = 0.0f;
    imageVertices[2] = 1.0f;
    imageVertices[3] = 0.0f;
    imageVertices[4] = 0.0f;
    imageVertices[5] = 1.0f;
    imageVertices[6] = 1.0f;
    imageVertices[7] = 1.0f;
    
    resetSourceRect();
    
    LBGraphics* g = LBGraphics::getInstance();
    calculateVertices();
    
    g->useDefaultProgram();
    setLocationAndScale(0, 0, 1, 1);
    
    positionAttribute = g->getPositionAttribute();
    texCoorAttribute = g->getTexCoorAttribute();
    textureUniform = g->getTextureUniform();
    
    textureActive = GL_TEXTURE0;
    isUseDefaultUniform = true;
    
    byteInAPixel = bytePerPixel;
    
    angle = 0;
    xScale = yScale = 1.0f;
    isDeleteTexIdWhenDeleteObj = true;
}

LBTexture::LBTexture(unsigned int texId, int width, int height){
    this->width = width;
    this->height = height;
    this->texId = texId;
    
    byteInAPixel = 4;
    
    mWorld = new Matrix();
    
    sX = dX = sY = dY = 0;
    sWidth = dWidth = width;
    sHeight = dHeight = height;
    
    dWidth = width;
    dHeight = height;
    
    glBindTexture(GL_TEXTURE_2D, texId);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    
    imageVertices[0] = 0.0f;
    imageVertices[1] = 0.0f;
    imageVertices[2] = 1.0f;
    imageVertices[3] = 0.0f;
    imageVertices[4] = 0.0f;
    imageVertices[5] = 1.0f;
    imageVertices[6] = 1.0f;
    imageVertices[7] = 1.0f;
    
    resetSourceRect();
    
    LBGraphics* g = LBGraphics::getInstance();
    calculateVertices();
    
    g->useDefaultProgram();
    setLocationAndScale(0, 0, 1, 1);
    
    positionAttribute = g->getPositionAttribute();
    texCoorAttribute = g->getTexCoorAttribute();
    textureUniform = g->getTextureUniform();
    
    textureActive = GL_TEXTURE0;
    isUseDefaultUniform = true;
    
    angle = 0;
    xScale = yScale = 1.0f;
    isDeleteTexIdWhenDeleteObj = true;
}

void LBTexture::setLocationAndScale(float x, float y, float scaleX, float scaleY){
    
    xScale = scaleX;
    yScale = scaleY;
    
    dX = x + scaleX*dWidth/2;
    dY = y + scaleY*dHeight/2;
    
    //calculateMatrix();
    
}

void LBTexture::setAngle(float angle){
    this->angle = angle;
    //calculateMatrix();
    
}

void LBTexture::render(){

    glActiveTexture(textureActive);
    glBindTexture(GL_TEXTURE_2D, texId);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    
    LBGraphics* g = LBGraphics::getInstance();
    
    calculateMatrix();
    calculateVertices();
    
    if(isUseDefaultUniform == true){
        positionAttribute = g->getPositionAttribute();
        texCoorAttribute = g->getTexCoorAttribute();
        textureUniform = g->getTextureUniform();
        mWorldUniform = g->getWorldUniform();
        mProjectionUniform = g->getProjectionUniform();
    }
    glUniformMatrix4fv(mWorldUniform, 1, GL_FALSE, &mWorld->m[0][0]);
    glUniformMatrix4fv(mProjectionUniform, 1, GL_FALSE, &(g->getProjectionMatrix()->m[0][0]));
    glUniform1i(textureUniform, 0);
    glVertexAttribPointer(positionAttribute, 2, GL_FLOAT, GL_FALSE, 0, imageVertices);
    glVertexAttribPointer(texCoorAttribute, 2, GL_FLOAT, GL_FALSE, 0, texCoors);
    
    glEnableVertexAttribArray(positionAttribute);
    glEnableVertexAttribArray(texCoorAttribute);
    
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    
    glDisableVertexAttribArray(positionAttribute);
    glDisableVertexAttribArray(texCoorAttribute);
}


GLuint LBTexture::getTexId(){
    return texId;
}

GLfloat* LBTexture::getImageVertices(){
    return imageVertices;
}

GLfloat* LBTexture::getTexCoors(){
    return texCoors;
}

LBTexture::~LBTexture(){
    if(isDeleteTexIdWhenDeleteObj)
        glDeleteTextures(1, &texId);
    DELETE_SAFELY(mWorld);
}

float LBTexture::getWidth(){
    return width;
}

float LBTexture::getHeight(){
    return height;
}

void LBTexture::setLocation(float x, float y){
    setLocationAndScale(x, y, 1.0f, 1.0f);
}

void LBTexture::renderUseDefaultProgram(){
    LBGraphics::getInstance()->useDefaultProgram();
    render();
}

void LBTexture::setTextureActive(int textureActive){
    this->textureActive = textureActive;
}

void LBTexture::setUniformAndAttribute(GLuint positionAttribute, GLuint texCoorAttribute, GLuint textureUniform){
    this->positionAttribute = positionAttribute;
    this->texCoorAttribute = texCoorAttribute;
    this->textureUniform = textureUniform;
    isUseDefaultUniform = false;
}

int LBTexture::getByteInAPixel(){
    return byteInAPixel;
}

void LBTexture::calculateMatrix(){
    mWorld->LoadIdentity();
    
    
    mWorld->Translate(dX, dY, 0.0);
    mWorld->Scale(xScale, yScale, 1.0f);
    mWorld->Rotate(angle, 0.0f, 0.0f, 1.0f);
}

Matrix* LBTexture::getWorldMatrix(){
    return mWorld;
}

void LBTexture::calculateVertices(){
    float left , right, top, bottom;
    left = -dWidth/2.0f;
    right = dWidth/2.0f;
    top = -dHeight/2.0f;
    bottom = dHeight/2.0f;
    
    imageVertices[0] = left;
    imageVertices[1] = bottom;
    imageVertices[2] = right;
    imageVertices[3] = bottom;
    imageVertices[4] = left;
    imageVertices[5] = top;
    imageVertices[6] = right;
    imageVertices[7] = top;
}

void LBTexture::setLocationByCenter(float xCenter, float yCenter){
    dX = xCenter;
    dY = yCenter;
}

void LBTexture::setScale(float xScale, float yScale){
    this->xScale = xScale;
    this->yScale = yScale;
}

void LBTexture::sourceRect(float x, float y, float width, float height){
    sX = x;
    sY = y;
    sWidth  = dWidth = width;
    sHeight = dHeight = height;
    
    float left , right, top, bottom;
    left = sX/this->width;
    right = (sX + width)/this->width;
    top = 1.0f - sY/(this->height);
    bottom = 1.0f - (sY + height)/(this->height);
    
    texCoors[0] = left;
    texCoors[1] = bottom;
    texCoors[2] = right;
    texCoors[3] = bottom;
    texCoors[4] = left;
    texCoors[5] = top;
    texCoors[6] = right;
    texCoors[7] = top;
}

void LBTexture::resetSourceRect(){
    sX = 0.0f;
    sY = 0.0f;
    sWidth = dWidth= width;
    sHeight = dHeight = height;
    
    texCoors[0] = 0.0f;
    texCoors[1] = 0.0f;
    texCoors[2] = 1.0f;
    texCoors[3] = 0.0f;
    texCoors[4] = 0.0f;
    texCoors[5] = 1.0f;
    texCoors[6] = 1.0f;
    texCoors[7] = 1.0f;
}

float LBTexture::getSourceHeight(){
    return sHeight;
}

float LBTexture::getSourceWidth(){
    return sWidth;
}

void LBTexture::setIsDeleteTexIdWhenDeleteObj(bool isDeleteTexIdWhenDeleteObj){
    this->isDeleteTexIdWhenDeleteObj = isDeleteTexIdWhenDeleteObj;
}